package com.google.code.swingonfire.utility.date;

import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.font.FontRenderContext;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;

import com.google.code.swingonfire.utility.Utils;

/**
 * Renders dates. As this class caches some values, this class is NOT thread-safe (at least, it is thread-safe as long
 * as the context does not change). You should change the context only if necessary (e.g. if the user changes the font).
 * If you need different contexts, create different render infos. It does not harm, if you set the same context multiple
 * times, as long as the context equals the last one.
 * 
 * @author thred
 */
public class DateRenderer
{

    public static DateRenderer create()
    {
        DateRenderer result = new DateRenderer();

        result.addDay(DateFormatStyle.SHORT);
        result.addConstant(".");
        result.addMonth(DateFormatStyle.LONG);
        result.addConstant(" ");
        result.addYear(DateFormatStyle.LONG);

        result.setLocale(Locale.getDefault());

        return result;
    }

    private final List<DateRendererPart> parts = new ArrayList<DateRendererPart>();

    private DateRendererYearPart yearPart;
    private DateRendererMonthPart monthPart;
    private DateRendererDayPart dayPart;
    private DateRendererContext context;

    protected DateRenderer()
    {
        super();

        context = new DateRendererContext(null, null, Locale.getDefault());
    }

    public void addConstant(String text)
    {
        parts.add(new DateRendererConstantPart(this, text));
    }

    public void addDay(DateFormatStyle style)
    {
        if (dayPart != null)
        {
            throw new IllegalArgumentException("More than one day field");
        }

        dayPart = new DateRendererDayPart(this);
        dayPart.setStyle(style);

        parts.add(dayPart);
    }

    public void addMonth(DateFormatStyle style)
    {
        if (monthPart != null)
        {
            throw new IllegalArgumentException("More than one month field");
        }

        monthPart = new DateRendererMonthPart(this);
        monthPart.setStyle(style);

        parts.add(monthPart);
    }

    public void addYear(DateFormatStyle style)
    {
        if (yearPart != null)
        {
            throw new IllegalArgumentException("More than one year field");
        }

        yearPart = new DateRendererYearPart(this);
        yearPart.setStyle(style);

        parts.add(yearPart);
    }

    public DateRendererContext getContext()
    {
        return context;
    }

    public void setContext(DateRendererContext context)
    {
        if (!Utils.equals(this.context, context))
        {
            this.context = context;
            resetCachedValues();
        }
    }

    public Font getFont()
    {
        return context.getFont();
    }

    public FontRenderContext getFontRenderContext()
    {
        return context.getFontRenderContext();
    }

    public void setFont(Font font, FontRenderContext fontRenderContext)
    {
        setContext(new DateRendererContext(font, fontRenderContext, context.getLocale()));
    }

    public Locale getLocale()
    {
        return context.getLocale();
    }

    public void setLocale(Locale locale)
    {
        setContext(new DateRendererContext(context.getFont(), context.getFontRenderContext(), locale));
    }

    public void setYearStyle(DateFormatStyle style)
    {
        if (yearPart.setStyle(style))
        {
            resetCachedValues();
        }
    }

    public void setMonthStyle(DateFormatStyle style)
    {
        if (monthPart.setStyle(style))
        {
            resetCachedValues();
        }
    }

    public void setDayStyle(DateFormatStyle style)
    {
        if (dayPart.setStyle(style))
        {
            resetCachedValues();
        }
    }

    private void resetCachedValues()
    {
        for (DateRendererPart part : parts)
        {
            part.resetCachedValues();
        }
    }

    public void render(Graphics2D g, Calendar calendar)
    {
        double x = 0;

        for (DateRendererPart part : parts)
        {
            x = part.render(g, calendar, x);
        }
    }
}
